package ferus.tigris.lines.levels;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

import android.graphics.Point;
import android.util.Log;
import ferus.tigris.lines.Builders.AbstractBehaviorBuilder;
import ferus.tigris.lines.Builders.BuilderWith7Builders;
import ferus.tigris.lines.Builders.BuilderWith8Builders;
import ferus.tigris.lines.GameManager;
import ferus.tigris.lines.Matrix;
import ferus.tigris.lines.data.PersonageInfo;
import ferus.tigris.lines.personages.AbstractBehavior;
import ferus.tigris.lines.personages.FriendsFinder;

public class StaticLevel implements AbstractLevel {
	private static final int QUEUE_SIZE = 3;
	private GameManager gameManager;
	private LevelManager levelManager;
	Matrix marks = new Matrix();
	
	static AbstractBehaviorBuilder builder = null;

	ArrayList<Matrix> queueMarks = new ArrayList<Matrix>();
	
	Deque<AbstractBehavior> queue = new ArrayDeque<AbstractBehavior>();
	private int scope = 0;
	private int initScope = 0;
	private int complexity = 100;

	public StaticLevel(LevelManager levelManager, GameManager gameManager, int scope, int complexity) {
		this.gameManager = gameManager;
		this.scope = scope;
		initScope = scope;
		this.complexity = complexity;
		this.levelManager = levelManager;
		
		for(int i = 0; i < QUEUE_SIZE; i++) {
			queueMarks.add(new Matrix(6,6));
		}
		
	}

	public AbstractLevel nextLevel() {
		return new LevelWithRandomInit(levelManager, gameManager, scope, getNextComplexity());
	}
	
	protected int getNextComplexity() {
		return (int)(complexity + (scope - initScope)*1.1);
	}

	public AbstractLevel clone() {
		return new StaticLevel(levelManager, gameManager, initScope(), complexity);
	}

	protected int initScope() {
		return initScope;
	}

	public void start() {
		fillMatrixByEmptyValues();
		
		fillQueue();
		
		gameManager.setPersonages(marks, queue);
	}

	protected void fillMatrixByEmptyValues() {
		AbstractBehaviorBuilder builder = createBuilder();
		for(int x = 0; x < marks.columns(); x++) {
			for(int y = 0; y < marks.rows(); y++) {
				AbstractBehavior mark = builder.create(gameManager.view());
				marks.insertMark(x, y, mark);
			}
		}
	}

	public int check() {
		fillQueue();
		if(isLevelComplited()) {
			levelManager.complite();
			return 0;
		}
		if(isLevelFail()) {
			levelManager.fail();
			return 0;
		}
		return checkGroupsForDied();
	}

	private boolean isLevelComplited() {
		return scope >= complexity;
	}

	private void fillQueue() {
		int i = 0;
		for (AbstractBehavior mark : queue) {
			Matrix m = queueMarks.get(i);
			mark.saveAt(m, new Point(i++, m.rows()));
		}
		
		while(queue.size() < QUEUE_SIZE) {
			AbstractBehaviorBuilder builder = createBuilder();
			AbstractBehavior mark = builder.createRandomMark(gameManager.view());
			Matrix m = queueMarks.get(queue.size());
			mark.saveAt(m, new Point(queue.size(), m.rows()));
			queue.add(mark);
		}
	}

	protected AbstractBehaviorBuilder createBuilder() {
		if(builder == null) {
			if(complexity < 120) {
				builder = new AbstractBehaviorBuilder();
			} else if(complexity < 140) {
				builder = new BuilderWith7Builders();
			} else {
				builder = new BuilderWith8Builders();
			}
		}
		return builder;
	}
	
	protected void changeBuilder(AbstractBehaviorBuilder newBuilder) {
		builder = newBuilder;
	}

	private int checkGroupsForDied() {
		int result = 0;
		for(int x = 0; x < marks.columns(); x++) {
			for(int y = 0; y < marks.rows(); y++) {
				if(marks.getMark(x, y).isDied())continue;
				for(int i = -1; i <= 1; i++) {
					for(int k = -1; k <= 1; k++) {
						FriendsFinder finder = new FriendsFinder();
						Point vector = new Point(i, k);
						List<AbstractBehavior>friends = finder.getFriends(marks, new Point(x, y), vector);
						if(friends.size() > 4) {
							for(AbstractBehavior b: friends) {
								b.kill();
							}
							result += friends.size();
						}
					}
				}
			}
		}
		scope += result;
		return result;
	}

	private boolean isLevelFail() {
		return isFieldFull(marks);
	}

	protected boolean isFieldFull(Matrix marks) {
		for(AbstractBehavior mark: marks.getMarks()) {
			if(mark.isDied()) {
				return false;
			}
		}
		return true;
	}

	public void died(AbstractBehavior personage) {
		AbstractBehaviorBuilder builder = createBuilder();
		for(int x = 0; x < marks.columns(); x++) {
			for(int y = 0; y < marks.rows(); y++) {
				if(marks.getMark(x, y).isDied()&&!marks.getMark(x, y).getType().contains("EmptyMark")) {
					AbstractBehavior mark = builder.create(gameManager.view());
					marks.insertMark(x, y, mark);
				}
			}
		}
	}

	public void onTouch(Point p) {
		if(p.y < 0)return;
		int elWidth = gameManager.view().getWidth() / marks.columns();
		int x = p.x / elWidth;
		int y = p.y / elWidth;
		
		if((y < 0)||(x < 0)||(x >= marks.columns()) || (y >= marks.rows()))return;
		if(!queue.isEmpty()) {
			if(marks.getMark(x, y).isDied()) {
				AbstractBehavior mark = queue.pop();
				marks.insertMark(x, y, mark);
				gameManager.view().sounds().playTouch();
			}
		}
	}

	public double scopeScale() {
		return 1.*scope/complexity;
	}

	protected Matrix marks() {
		return marks;
	}

	public void fill(List<PersonageInfo> personages) {
		AbstractBehaviorBuilder builder = createBuilder();
		for(PersonageInfo info: personages) {
			if(marks.valid(info.x, info.y)) {
				AbstractBehavior mark = builder.createByName(gameManager.view(), info.name);
				Log.d(getClass().toString(), "created " + mark.getType());
				marks.insertMark(info.x, info.y, mark);
			}
		}
	}

	public List<PersonageInfo> personages() {
		List<PersonageInfo> res = new ArrayList<PersonageInfo>();
		for(int x = 0; x < marks.columns(); x++) {
			for(int y = 0; y < marks.rows(); y++) {
				AbstractBehavior mark = marks.getMark(x, y);
				PersonageInfo info = new PersonageInfo();
				info.name = mark.getType();
				info.x = x;
				info.y = y;
				res.add(info);
			}
		}
		return res;
	}

	public void setScope(int scope) {
		this.scope = scope;
		
	}

	public int scope() {
		return scope;
	}

	public int complexity() {
		return complexity;
	}

	public void onLoad() {
		fillQueue();
		gameManager.setPersonages(marks, queue);
	}

	public double scaleScope() {
		return 1.0*(scope-initScope)/(complexity - initScope);
	}

}
